package com.wxzhou.wangzhuan.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.wxzhou.wangzhuan.dto.config.ThirdPayConfigDto;
import com.wxzhou.wangzhuan.dto.pay.*;
import com.wxzhou.wangzhuan.entity.RealNameInfo;
import io.smallrye.mutiny.Uni;
import io.vertx.core.json.JsonObject;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.jboss.logging.Logger;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 * 支付服务，策略模式
 */
@ApplicationScoped
public class PayService {
    private static final Logger LOG = Logger.getLogger(PayService.class);

    @Inject
    ConfigService configService;

    @Inject
    OkHttpClientFactory httpClientFactory;

    @Inject
    OkHttpConfig config;

    @Inject
    ObjectMapper objectMapper;

    private static final String AES = "AES";
    private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
    private static final MediaType JSON = MediaType.get("application/json; charset=utf-8");

    /**
     * AES加密
     */
    private String aesEncode(String key, String content, String iv) {
        try {
            SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), AES);
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv.getBytes()));

            // 获取加密内容的字节数组
            byte[] byteEncode = content.getBytes(java.nio.charset.StandardCharsets.UTF_8);

            // 根据密码器的初始化方式加密
            byte[] byteAES = cipher.doFinal(byteEncode);

            // 将加密后的数据转换为Base64字符串
            return Base64.getEncoder().encodeToString(byteAES);
        } catch (Exception e) {
            LOG.error("AES加密失败: " + e.getMessage(), e);
        }
        return null;
    }

    /**
     * AES解密
     */
    private String aesDecode(String key, String content, String iv) {
        try {
            SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), AES);
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv.getBytes()));

            // 将加密并编码后的内容解码成字节数组
            byte[] byteContent = Base64.getDecoder().decode(content);
            // 解密
            byte[] byteDecode = cipher.doFinal(byteContent);
            return new String(byteDecode, java.nio.charset.StandardCharsets.UTF_8);
        } catch (Exception e) {
            LOG.error("AES解密失败: " + e.getMessage(), e);
        }
        return null;
    }

    /**
     * MD5加密
     */
    public static String md5(String str) {
        try {
            MessageDigest digest = MessageDigest.getInstance("MD5");
            digest.update(str.getBytes());
            return new BigInteger(1, digest.digest()).toString(16);
        } catch (NoSuchAlgorithmException e) {
            LOG.error("MD5加密失败: " + e.getMessage(), e);
        }
        return null;
    }

    /**
     * 获取签名
     */
    private String generateSign(Map<String, Object> params, String key, String timeStamp) {
        StringBuilder sbkey = new StringBuilder();
        sbkey.append(timeStamp);

        // 使用TreeMap对参数进行ASCII排序
        TreeMap<String, Object> sortedParams = new TreeMap<>(params);
        Set<Map.Entry<String, Object>> es = sortedParams.entrySet();
        Iterator<Map.Entry<String, Object>> it = es.iterator();

        while (it.hasNext()) {
            Map.Entry<String, Object> entry = it.next();
            Object v = entry.getValue();
            // 空值不传递，不参与签名组串
            if (null != v && !"".equals(v)) {
                sbkey.append(v);
            }
        }

        sbkey.append(key);
        return md5(sbkey.toString()).toUpperCase();
    }

    /**
     * 通用支付API调用方法
     *
     * @param <T>           响应数据类型
     * @param <R>           请求参数类型
     * @param apiPath       API路径
     * @param requestParams 请求参数对象
     * @param responseType  响应数据类型
     * @param userId        用户ID，用于并发控制
     * @return 封装了目标对象的HuifuResponseDto
     */
    public <T, R> Uni<HuifuResponseDto<T>> callHuifuApi(String apiPath, R requestParams, Class<T> responseType, Long userId) {
        return configService.getThirdPayConfig().onItem().transformToUni(thirdPayConfigDto -> {
            return callHuifuApiWithConfig(thirdPayConfigDto, apiPath, requestParams, responseType, userId);
        });
    }

    /**
     * 使用指定配置的支付API调用方法
     */
    public <T, R> Uni<HuifuResponseDto<T>> callHuifuApiWithConfig(ThirdPayConfigDto configDto, String apiPath, R requestParams, Class<T> responseType, Long userId) {

        try {
            // 将请求参数对象转换为Map
            Map<String, Object> paramsMap = objectMapper.convertValue(requestParams, Map.class);

            // 构建完整的请求URL
            String requestUrl = configDto.getApiUrl() + apiPath;
            String userIdStr = userId != null ? String.valueOf(userId) : "default";

            // 获取全局和单源的信号量，用于并发控制
            return Uni.createFrom().item(() -> {
                Semaphore globalSemaphore = httpClientFactory.getGlobalSemaphore();
                Semaphore sourceSemaphore = httpClientFactory.getSourceSemaphore(userIdStr);

                try {
                    // 尝试获取全局并发许可，超时时间为配置的调用超时时间
                    if (!globalSemaphore.tryAcquire(config.callTimeout(), TimeUnit.MILLISECONDS)) {
                        throw new RuntimeException("全局并发限制");
                    }
                    // 尝试获取单源并发许可
                    if (!sourceSemaphore.tryAcquire(config.callTimeout(), TimeUnit.MILLISECONDS)) {
                        // 如果获取单源许可失败，需要释放已获取的全局许可
                        globalSemaphore.release();
                        throw new RuntimeException("源并发限制");
                    }
                    return new SemaphorePair(globalSemaphore, sourceSemaphore);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException("获取并发许可被中断", e);
                }
            }).onItem().transformToUni(semaphorePair -> {
                // 执行HTTP请求
                return executeHuifuHttpRequest(configDto, paramsMap, requestUrl, responseType).onTermination().invoke(() -> {
                    // 请求完成后释放信号量
                    semaphorePair.globalSemaphore.release();
                    semaphorePair.sourceSemaphore.release();
                });
            }).onFailure().recoverWithItem(throwable -> {
                LOG.error("调用汇付API失败: " + throwable.getMessage(), throwable);
                return HuifuResponseDto.error("调用汇付API失败: " + throwable.getMessage());
            });
        } catch (Exception e) {
            LOG.error("准备汇付API请求失败: " + e.getMessage(), e);
            return Uni.createFrom().item(HuifuResponseDto.error("准备汇付API请求失败: " + e.getMessage()));
        }
    }

    /**
     * 执行汇付HTTP请求
     */
    private <T> Uni<HuifuResponseDto<T>> executeHuifuHttpRequest(ThirdPayConfigDto configDto, Map<String, Object> paramsMap, String requestUrl, Class<T> responseType) {

        return Uni.createFrom().item(() -> {
            long startTime = System.currentTimeMillis();

            try {
                // 1. 准备请求数据
                // 将参数对象转换为JSON字符串
                String paramsJson = objectMapper.writeValueAsString(paramsMap);

                // 2. 使用apiSecret(对应demo中的comSecret)进行AES加密
                String encryptedData = aesEncode(configDto.getApiSecret(), paramsJson, configDto.getApiKey());
                if (encryptedData == null) {
                    throw new RuntimeException("参数加密失败");
                }

                // 3. 构建请求体
                Map<String, Object> requestBodyMap = new HashMap<>();
                long timestamp = System.currentTimeMillis() / 1000;
                requestBodyMap.put("comKey", configDto.getApiKey());
                requestBodyMap.put("timestamp", timestamp);
                requestBodyMap.put("data", encryptedData);

                // 4. 转换为JSON字符串
                String requestBodyJson = objectMapper.writeValueAsString(requestBodyMap);

                // 5. 构建HTTP POST请求
                RequestBody body = RequestBody.create(requestBodyJson, JSON);
                Request request = new Request.Builder().url(requestUrl).post(body).addHeader("Content-type", "application/json").addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)").build();

                // 6. 执行请求
                try (Response response = httpClientFactory.getHttpClient().newCall(request).execute()) {
                    long duration = System.currentTimeMillis() - startTime;

//                    // 7. 记录慢请求日志
//                    if (config.logEnabled() && duration > config.logSlowThreshold()) {
//                        LOG.warn("汇付API调用耗时过长: " + requestUrl + ", 耗时: " + duration + "ms");
//                    }

                    // 8. 检查HTTP响应状态
                    if (response.isSuccessful() && response.body() != null) {
                        String responseBody = response.body().string();
                        return parseResponse(responseBody, responseType);
                    } else {
                        String errorMsg = "HTTP状态码: " + response.code();
                        if (response.body() != null) {
                            errorMsg += ", 响应: " + response.body().string();
                        }
                        return HuifuResponseDto.fail(errorMsg);
                    }
                }
            } catch (Exception e) {
                LOG.error("执行汇付HTTP请求异常: " + e.getMessage(), e);
                return HuifuResponseDto.fail("请求异常: " + e.getMessage());
            }
        });
    }

    /**
     * 获取授权链接
     */
    public Uni<HuifuResponseDto> getScheme(Long userid) {
        // 创建请求参数对象
        HuifuWeixinSchemeReq requestParams = new HuifuWeixinSchemeReq();
        requestParams.setAccount(String.valueOf(userid));

        // 调用通用API方法
        return configService.getThirdPayConfig().onItem().transformToUni(thirdPayConfigDto -> {
            // 设置应用ID和商户号
            requestParams.setAppid(thirdPayConfigDto.getAppid());
            requestParams.setMerchant_id(thirdPayConfigDto.getMerchantId());

            // 调用通用API方法，使用RealNameCheckResponseDto作为响应类型（临时使用，实际应根据API返回定义专门的响应类型）
            return callHuifuApi("/api/mp/getScheme", requestParams, HuifuSchemeRespDto.class, userid).onItem().transform(result -> {
                if (result.isSuccess() && result.getData() != null) {
                    // 解析成功，直接返回结果
                    return result;
                } else {
                    // 解析失败，返回错误信息
                    return HuifuResponseDto.fail("获取错误: " + (result.getmessage() != null ? result.getmessage() : "未知错误"), result.getCode() != 0 ? result.getCode() : 500);
                }
            });
        });
    }

    /**
     * 用户同意纳税协议才请求该接口.否则不予以调用.
     *
     * @param userid
     * @return
     */
    public Uni<HuifuResponseDto<HuifuTaxAgreementRespDto>> taxAgreement(Long userid) {
        // 创建请求参数对象
        HuifuTaxAgreementReq requestParams = new HuifuTaxAgreementReq();
        requestParams.setAccount(String.valueOf(userid));

        // 调用通用API方法
        return configService.getThirdPayConfig().onItem().transformToUni(thirdPayConfigDto -> {
            // 设置应用ID和商户号
            requestParams.setAppid(thirdPayConfigDto.getAppid());

            // 调用通用API方法，使用RealNameCheckResponseDto作为响应类型（临时使用，实际应根据API返回定义专门的响应类型）
            return callHuifuApi("/api/taxAgreement", requestParams, HuifuTaxAgreementRespDto.class, userid).onItem().transform(result -> {
                if (result.isSuccess() && result.getData() != null) {
                    // 解析成功，直接返回结果
                    return result;
                } else {
                    // 解析失败，返回错误信息
                    return HuifuResponseDto.fail("获取错误: " + (result.getmessage() != null ? result.getmessage() : "未知错误"), result.getCode() != 0 ? result.getCode() : 500);
                }
            });
        });
    }

    /**
     * 根据提现金额评估本次扣税情况
     *
     * @param userid
     * @param amount
     * @return
     */
    public Uni<HuifuResponseDto<HuifuTaxRespDto>> getTax(Long userid, Integer amount) {
        // 创建请求参数对象
        HuifuTaxReq requestParams = new HuifuTaxReq();
        requestParams.setAccount(String.valueOf(userid));
        requestParams.setAmount(amount);

        // 调用通用API方法
        return configService.getThirdPayConfig().onItem().transformToUni(thirdPayConfigDto -> {
            // 设置应用ID和商户号
            requestParams.setAppid(thirdPayConfigDto.getAppid());

            // 调用通用API方法，使用RealNameCheckResponseDto作为响应类型（临时使用，实际应根据API返回定义专门的响应类型）
            return callHuifuApi("/api/getTax", requestParams, HuifuTaxRespDto.class, userid).onItem().transform(result -> {
                if (result.isSuccess() && result.getData() != null) {
                    // 解析成功，直接返回结果
                    return result;
                } else {
                    // 解析失败，返回错误信息
                    return HuifuResponseDto.fail("获取错误: " + (result.getmessage() != null ? result.getmessage() : "未知错误"), result.getCode() != 0 ? result.getCode() : 500);
                }
            });
        });
    }

    /**
     * 提交实名
     *
     * @param userid
     * @return
     */
    public Uni<HuifuResponseDto> submitRealName(Long userid, RealNameInfo realNameInfo) {
        // 创建请求参数对象
        HuifuRealnameReq requestParams = new HuifuRealnameReq();
        requestParams.setOpenid(String.valueOf(userid));

        // 调用通用API方法
        return configService.getThirdPayConfig().onItem().transformToUni(thirdPayConfigDto -> {
            // 设置应用ID和商户号
            requestParams.setAppid(thirdPayConfigDto.getAppid());
            requestParams.setName(realNameInfo.getRealname());
            requestParams.setPhone(realNameInfo.getPhone());
            requestParams.setIdcard(realNameInfo.getCardNumber());

            // 调用通用API方法，使用RealNameCheckResponseDto作为响应类型（临时使用，实际应根据API返回定义专门的响应类型）
            return callHuifuApi("/api/eqbApprove", requestParams, HuifuRealnameRespDto.class, userid).onItem().transform(result -> {
                if (result.isSuccess() && result.getData() != null) {
                    // 解析成功，直接返回结果
                    return result;
                } else {
                    // 解析失败，返回错误信息
                    return HuifuResponseDto.fail("获取错误: " + (result.getmessage() != null ? result.getmessage() : "未知错误"), result.getCode() != 0 ? result.getCode() : 500);
                }
            });
        });
    }

    /**
     * 解析响应体，将其转换为指定类型的对象并封装在HuifuResponseDto中
     *
     * @param responseBody 响应体字符串
     * @param cls          目标对象的Class类型
     * @param <T>          目标对象的泛型类型
     * @return 封装了目标对象的HuifuResponseDto
     */
    private <T> HuifuResponseDto<T> parseResponse(String responseBody, Class<T> cls) {
        try {
            LOG.debugf("响应参数:" + responseBody);
            // 将响应体解析为JSON对象
            JsonObject json = new JsonObject(responseBody);
            int code = json.getInteger("code", 500);
            String message = json.getString("message", "");

            if (code == 200) {
                // 解析成功，将data字段转换为预期的对象
                if (json.containsKey("data")) {
                    JsonObject dataJson = json.getJsonObject("data");
                    if (dataJson != null) {
                        // 使用Jackson的ObjectMapper将Json对象转换为指定类型
                        String dataJsonStr = dataJson.encode();
                        T data = objectMapper.readValue(dataJsonStr, cls);
                        return HuifuResponseDto.success(data, message);
                    }
                }
                // 没有data字段或data为null时，返回成功但data为null
                return HuifuResponseDto.success(null, message);
            }
            // 响应失败
            return HuifuResponseDto.fail(message, code);
        } catch (Exception e) {
            LOG.error("解析响应体失败: " + e.getMessage(), e);
            return HuifuResponseDto.error("解析响应体失败: " + e.getMessage());
        }
    }


}
